﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk.Workflow;
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;

/**
 * Copyright 2011 by Alberto Gemin
 * agemin@hotmail.com
 * Version 1.2
 * 27 Aug 2011
 **/
namespace Gemina.CRM2011.WF.CRM2011ShareStep
{
    [AgCodeActivity("Share Record", "AG Utilities")]
    public class ShareRecordActivity : CodeActivityBase
    {

        #region Workflow Parameters

        /// <summary>
        /// Teams to share with.
        /// </summary>
        [Input("Share Team 1")]
        [ReferenceTarget("team")]
        public InArgument<EntityReference> Team1 { get; set; }

        /// <summary>
        /// Teams to share with.
        /// </summary>
        [Input("Share Team 2")]
        [ReferenceTarget("team")]
        public InArgument<EntityReference> Team2 { get; set; }

        /// <summary>
        /// Users to share with.
        /// </summary>
        [Input("Share User 1")]
        [ReferenceTarget("systemuser")]
        public InArgument<EntityReference> User1 { get; set; }

        /// <summary>
        /// Users to share with.
        /// </summary>
        [Input("Share User 2")]
        [ReferenceTarget("systemuser")]
        public InArgument<EntityReference> User2 { get; set; }

        /// <summary>
        /// If true remove all shares before sharing the record.
        /// </summary>
        [Input("Remove All Shares")]
        [Default("False")]
        public InArgument<bool> RemoveShares { get; set; }

        /// <summary>
        /// Share Read privilege.
        /// </summary>
        [Input("Read Permission")]
        [Default("True")]
        public InArgument<bool> ShareRead { get; set; }

        /// <summary>
        /// Share Write privilege.
        /// </summary>
        [Input("Write Permission")]
        [Default("False")]
        public InArgument<bool> ShareWrite { get; set; }

        /// <summary>
        /// Share Delete privilege.
        /// </summary>
        [Input("Delete Permission")]
        [Default("False")]
        public InArgument<bool> ShareDelete { get; set; }

        /// <summary>
        /// Share Append privilege.
        /// </summary>
        [Input("Append Permission")]
        [Default("False")]
        public InArgument<bool> ShareAppend { get; set; }

        /// <summary>
        /// Share AppendTo privilege.
        /// </summary>
        [Input("Append To Permission")]
        [Default("False")]
        public InArgument<bool> ShareAppendTo { get; set; }

        /// <summary>
        /// Share Assign privilege.
        /// </summary>
        [Input("Assign Permission")]
        [Default("False")]
        public InArgument<bool> ShareAssign { get; set; }

        /// <summary>
        /// Share Share privilege.
        /// </summary>
        [Input("Share Permission")]
        [Default("False")]
        public InArgument<bool> ShareShare { get; set; }

        #endregion

        #region CodeActivity

        override protected bool ExecuteBody(CodeActivityContext executionContext)
        {
            PrincipalAccess[] principals = GetPrincipals();

            if (this.RemoveShares.Get(executionContext))
            {
                RemovePrincipals(principals);
            }
            AddPrincipals(executionContext);

            return true;
        }

        #endregion

        #region Local Methods

        private PrincipalAccess[] GetPrincipals()
        {
            RetrieveSharedPrincipalsAndAccessRequest retrieve = new RetrieveSharedPrincipalsAndAccessRequest();
            retrieve.Target = new EntityReference(this.CurrentEntityName, this.CurrentRecordId);

            RetrieveSharedPrincipalsAndAccessResponse retrieved = (RetrieveSharedPrincipalsAndAccessResponse)this.OrganizationService.Execute(retrieve);
            return retrieved.PrincipalAccesses;
        }

        private void RemovePrincipals(PrincipalAccess[] principals)
        {
            RevokeAccessRequest request = new RevokeAccessRequest();
            request.Target = new EntityReference(this.CurrentEntityName, this.CurrentRecordId);
            foreach (PrincipalAccess principal in principals)
            {
                if (principal.Principal.LogicalName == "user")
                {
                    principal.Principal.LogicalName = "team"; // very weird but that's the way it is
                }
                request.Revokee = principal.Principal;
                Object o = this.OrganizationService.Execute(request);
                RevokeAccessResponse response = (RevokeAccessResponse)o;
            }
        }

        private void AddPrincipal(List<EntityReference> principals, EntityReference principal)
        {
            if (principal != null)
            {
                principals.Add(principal);
            }
        }

        private void AddPrincipals(CodeActivityContext executionContext)
        {
            List<EntityReference> principals = new List<EntityReference>();

            this.AddPrincipal(principals, User1.Get(executionContext));
            this.AddPrincipal(principals, User2.Get(executionContext));
            this.AddPrincipal(principals, Team1.Get(executionContext));
            this.AddPrincipal(principals, Team2.Get(executionContext));

            if (principals.Count > 0)
            {
                UInt32 mask = 0;
                if (ShareAppend.Get(executionContext))
                {
                    mask |= (UInt32)AccessRights.AppendAccess;
                }
                if (ShareAppendTo.Get(executionContext))
                {
                    mask |= (UInt32)AccessRights.AppendToAccess;
                }
                if (ShareAssign.Get(executionContext))
                {
                    mask |= (UInt32)AccessRights.AssignAccess;
                }
                if (ShareDelete.Get(executionContext))
                {
                    mask |= (UInt32)AccessRights.DeleteAccess;
                }
                if (ShareRead.Get(executionContext))
                {
                    mask |= (UInt32)AccessRights.ReadAccess;
                }
                if (ShareShare.Get(executionContext))
                {
                    mask |= (UInt32)AccessRights.ShareAccess;
                }
                if (ShareWrite.Get(executionContext))
                {
                    mask |= (UInt32)AccessRights.WriteAccess;
                }

                if (mask != 0)
                {
                    GrantAccessRequest request = new GrantAccessRequest();
                    request.Target = new EntityReference(this.CurrentEntityName, this.CurrentRecordId);

                    request.PrincipalAccess = new PrincipalAccess();
                    request.PrincipalAccess.AccessMask = (AccessRights)mask;

                    foreach (EntityReference principal in principals)
                    {
                        request.PrincipalAccess.Principal = principal;
                        GrantAccessResponse response = (GrantAccessResponse)this.OrganizationService.Execute(request);
                    }
                }
            }
        }

        #endregion

    }
}
